package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RefreshScope
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public OrderInfoVo trade(Long userId, TradeVo tradeVo) {

        //1.初始化OrderInfoVo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //1.1 初始化订单相关金额（原价、减免价、订单价）变量  TODO：金额初始一定是"0.00"
        BigDecimal originalAmount = new BigDecimal("0.00");
        BigDecimal derateAmount = new BigDecimal("0.00");
        BigDecimal orderAmount = new BigDecimal("0.00");
        //1.2 初始化订单明细、优惠明细对象集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //项目类型: 1001-专辑 1002-声音 1003-vip会员
        String itemType = tradeVo.getItemType();
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.处理购买项目类型为会员套餐
            //2.1 远程调用用户服务获取套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "会员套餐不存在");
            //2.2 封装"商品"相关价格
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            //是否有优惠 采用BigDecimal提供比较大小方法compareTo
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //2.3 封装商品明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP套餐：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            //订单明细中价格设置为原价
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);
            //2.4 封装优惠明细列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("套餐限时优惠：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3.处理购买项目类型为专辑
            //3.1 远程调用“用户”服务是否已购专辑,已购买业务终止，给出错误提示信息
            Long albumId = tradeVo.getItemId();
            Boolean flag = userFeignClient.isPaidAlbum(albumId).getData();
            if (flag) {
                throw new RuntimeException("您已购买过该专辑，请勿重复购买");
            }
            //3.2 远程调用"专辑"服务获取专辑信息 得到价格及折扣（普通用户折扣，VIP会员折扣）
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑不存在");
            BigDecimal price = albumInfo.getPrice();
            BigDecimal discount = albumInfo.getDiscount();
            BigDecimal vipDiscount = albumInfo.getVipDiscount();

            //3.3 远程调用用户服务得到用户信息，判断用户是否为会员
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户不存在");
            Boolean isVIP = false;
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //3.4 计算相关价格 订单价=原价*折扣  订单价=100*8/ 10
            originalAmount = price;
            orderAmount = originalAmount;
            if (!isVIP && discount.doubleValue() != -1) {
                // 避免专辑价格存在小数情况设置保留小数位以及设置舍入模式
                orderAmount = originalAmount.multiply(discount).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
            }
            if (isVIP && vipDiscount.doubleValue() != -1) {
                orderAmount = originalAmount.multiply(vipDiscount).divide(new BigDecimal("10"), 2, BigDecimal.ROUND_HALF_UP);
            }
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
            }
            //3.5 封装商品明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);
            //3.6 封装优惠明细列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑限时优惠：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.处理购买项目类型为声音
            //4.1 远程调用专辑服务获取用户指定购买集数对应未购买声音列表
            List<TrackInfo> trackInfoList = albumFeignClient.getWaitBuyTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount()).getData();

            //4.2 远程调用专辑服务获取专辑价格（声音单价）
            //从声音列表中第一条声音记录获取所属专辑ID
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            Assert.notNull(albumInfo, "专辑不存在");
            BigDecimal price = albumInfo.getPrice();

            //4.3 封装相关价格：原价、订单价  声音不支持折扣
            originalAmount = price.multiply(BigDecimal.valueOf(trackInfoList.size()));
            orderAmount = originalAmount;

            //4.4 基于得到未购买声音列表封装订单明细列表
            orderDetailVoList = trackInfoList
                    .stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音：" + trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());

        }
        //5.封装OrderInfoVo对象
        //5.1 封装订单相关金额
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        //5.2 封装商品相关订单明细、优惠列表
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //5.3 封装其他杂项信息
        orderInfoVo.setItemType(itemType);
        //5.4 封装流水号-目的解决由于回退按钮造成订单重复提交
        //5.4.1 构建当前用户本次订单流水号key 形式=前缀:用户ID
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //5.4.2 随机产生UUID作为流水号的值
        String tradeNo = IdUtil.randomUUID();
        //5.4.2 将流水号信息存入Redis 过期时间为5分钟
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConstant.ORDER_TRADE_EXPIRE, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);
        //5.5 时间戳及签名 防止订单确认页表单中信息被恶意篡改
        //5.5.1 设置本次订单时间戳
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        //5.5.2 将订单VO信息转为Map TODO 由于订单VO中包含属性payWay 结算页无法确定支付方式，故付款方式不需要参与签名
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
        //5.5.3 调用签名工具类设置本次订单签名
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }

    @Value("${order.cancel}")
    private Integer cancelOrderTTL;
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //1.业务校验：验证流水号是否相同，避免回退造成订单重复提交
        //1.1 获取用户提交流水号
        String tradeNo = orderInfoVo.getTradeNo();
        //1.2 构建当前用户订单流水号key 形式=前缀:用户ID 查询Redis中正确流水号
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.3 采用Lua脚本比较通过删除 保证判断跟删除原子性
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
        script.setScriptText(scriptText);
        script.setResultType(Boolean.class);
        Boolean flag = (Boolean) redisTemplate.execute(script, Arrays.asList(tradeKey), tradeNo);
        if (!flag) {
            throw new GuiguException(ResultCodeEnum.ORDER_RADE_NO_ERROR);
        }
        //2.业务校验：验证签名，避免订单确认页提交表单信息被恶意篡改
        //2.1 将订单VO信息转为Map TODO 由于加签时候没有加入付款方式"payWay" 验签要将其排除掉
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        map.remove("payWay");
        //2.2 调用工具类验签方法 验签失败会自动抛出异常！
        SignHelper.checkSign(map);

        //3.TODO 核心业务1:将提交订单相关信息存入数据库 包括：订单（未支付）、订单明细、订单优惠明细
        OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo, userId);

        //4.处理用户付款方式为余额
        // 支付方式：1101-微信 1102-支付宝 1103-账户余额
        String payWay = orderInfoVo.getPayWay();
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)) {
            //4.1 核心业务2：远程调用账户服务，扣减账户余额
            //4.1.1 构建扣减余额VO对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());

            //4.1.2 发起远程调用
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            //4.1.3 由于存在全局异常处理，判断业务状态码是否为200
            if (!result.getCode().equals(200)) {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LESS);
            }
            //4.2 扣减余额成功，更新订单状态（已支付）
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //4.3 核心业务3：虚拟物品发货（新增VIP会员、声音、专辑购买记录）
            //4.3.1 构建虚拟物品发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //
            List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
            if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
                List<Long> itemIdList = orderDetailVoList.
                        stream()
                        .map(OrderDetailVo::getItemId)
                        .collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(itemIdList);
            }
            //4.3.2 发起远程调用
            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            //4.3.3 由于存在全局异常处理，判断业务状态码是否为200
            if (!result.getCode().equals(200)) {
                throw new GuiguException(220, "虚拟物品发货异常！");
            }
        }
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)) {
            //5.TODO 处理用户付款方式为微信（根据产生订单编号对接微信支付，避免用户不付款采用延迟消息进行延迟关单）

            rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER,MqConst.ROUTING_CANCEL_ORDER,orderInfo.getId(),cancelOrderTTL);


        }
        //6.返回订单编号
        Map<String, String> mapResult = new HashMap<>();
        mapResult.put("orderNo", orderInfo.getOrderNo());
        return mapResult;
    }

    @Override
    public OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
        //1.保存订单
        //1.1 将订单VO转为订单PO对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //1.2 为订单其他属性赋值
        //1.2.1 用户ID
        orderInfo.setUserId(userId);
        //1.2.2 订单标题（从订单明细中获取商品名称）
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            String itemName = orderDetailVoList.get(0).getItemName();
            orderInfo.setOrderTitle(itemName);
        }
        //1.2.3 订单编号（确保全局唯一且趋势递增） 形式：日期+雪花算法
        String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        //1.2.4 订单状态（未支付）
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //1.3 保存订单
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();
        //2.保存订单明细
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                //2.1 将订单明细VO转为订单明细PO对象 关联订单ID
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                orderDetail.setOrderId(orderId);
                //2.2 保存订单明细
                orderDetailMapper.insert(orderDetail);
            }
        }
        //3.保存订单优惠
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
            for (OrderDerateVo orderDerateVo : orderDerateVoList) {
                //3.1 将订单优惠VO转为订单优惠PO对象 关联订单ID
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                orderDerate.setOrderId(orderId);
                //3.2 保存订单优惠
                orderDerateMapper.insert(orderDerate);
            }
        }
        return orderInfo;
    }

    @Override
    public void cancelOrder(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo != null&&SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())){
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
        }
    }

    @Override
    public OrderInfo getOrderInfo(String orderNo) {

        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));

        if (orderInfo!=null){
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfo.getId()));
            orderInfo.setOrderDetailList(orderDetailList);
        }

        return orderInfo;


    }

    @Override
    public Page<OrderInfo> getOrderByUserPage(Page<OrderInfo> pageInfo, Long userId) {
        return orderInfoMapper.getOrderByUserPage(pageInfo,userId);
    }

    /**
     * 用户在线支付成功后，更新订单状态及进行虚拟物品发货
     *
     * @param orderNo 订单编号
     * @return
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.根据订单编号查询订单信息
        OrderInfo orderInfo = this.getOrderInfo(orderNo);

        //2.将订单状态更新为已支付
        // 业务选择1：支付成功，即使刚被延迟消息关单，也正常发货。
        // 业务选择2：如果订单已取消，则不发货，进行退款处理；更新本地交易记录已关闭
        if (orderInfo != null) {
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            this.updateById(orderInfo);

            //3.远程调用用户服务进行虚拟物品发货
            //3.1 构建虚拟物品发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //获取订单中订单明细中购买项目ID列表
            List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
            if (CollectionUtil.isNotEmpty(orderDetailList)) {
                List<Long> itemIdList = orderDetailList
                        .stream()
                        .map(OrderDetail::getItemId)
                        .collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(itemIdList);
            }
            //3.2 远程调用用户服务进行虚拟物品发货
            Result result = userFeignClient.savePaidRecord(userPaidRecordVo);

            //3.3 判断响应业务状态码是否为200 如果不是200抛出异常
            if (!result.getCode().equals(200)) {
                throw new GuiguException(234, "虚拟物品发货异常");
            }
        }
    }

}
